
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "bsp_rtc.h"
#include "SEGGER_RTT.h"

#define DRM_LOG_BUFFER_LEN (4 * ULOG_MAX_MESSAGE_LENGTH)

static void drm_console_logger(ulog_level_t severity, char *time, char *msg);
static void drm_cache_logger(ulog_level_t severity, char *time, char *msg);
static void drm_jlink_rtt_logger(ulog_level_t severity, char *time, char *msg);

static int16_t drm_log_write_to_queue(char *buff, uint16_t len);

static QueueHandle_t log_xqueue = NULL;
static char *drm_log_buff = NULL;

/**
 * @brief 增加缓存队列,
 * 避免频繁操作flash
 * 现在判断 DRM_LOG_BUFFER_LEN 做一次写操作
 */
void drm_log_init(void)
{
    log_xqueue = xQueueCreate((UBaseType_t)(DRM_LOG_BUFFER_LEN), (UBaseType_t)(sizeof(uint8_t)));

    ULOG_INIT();
    // ULOG_SUBSCRIBE(drm_console_logger, ULOG_TRACE_LEVEL);
    //ULOG_SUBSCRIBE(drm_cache_logger, ULOG_TRACE_LEVEL);
    ULOG_SUBSCRIBE(drm_jlink_rtt_logger, ULOG_TRACE_LEVEL);

    for (uint8_t i = 0; i < FILE_DIR_LOG_NUMBER; i++)
    {
        /* 初始化文件保存数组 */
        char *file_log_name = file_get_log_name(i);
        memset(file_log_name, '\0', FILE_LOG_NAME_SZIE);

        /* 初始化文件名保存时间 */
        file_dir_log_info()->log_info[i].time_temp = 0;
    }
}

/**
 * @brief 将数据放入队列
 *
 * @param buff
 * @return int16_t
 */
static int16_t drm_log_write_to_queue(char *buff, uint16_t len)
{
    if (log_xqueue == NULL)
        return -1;
    if (buff == NULL)
        return -2;
    for (uint16_t i = 0; i < len; i++)
    {
        xQueueSend(log_xqueue, &buff[i], 0);
    }
    return 0;
}

/**
 * @brief 获取队列的长度
 *
 * @return int16_t
 */
int16_t drm_log_get_queue_length(void)
{
    int16_t len = 0;

    if (log_xqueue == NULL)
        return -1;

    len = uxQueueMessagesWaiting(log_xqueue);

    return len;
}

/**
 * @brief 出队
 *
 * @param buff
 * @param len
 * @return int16_t
 */
int16_t drm_log_queue_puts_data(uint8_t *buff, uint16_t len)
{
    if (log_xqueue == NULL)
        return -1;

    for (uint16_t i = 0; i < len; i++)
    {
        BaseType_t res = xQueueReceive(log_xqueue, &buff[i], (TickType_t)(1000));
        if (res == pdFALSE)
        {
            return i;
        }
    }
    return len;
}

/**
 * @brief 1.判断是否要写文件;
 * 2.返回 是否有写操作;
 * @param fil
e_log
 * @param lfs
 * @return int32_t
 */
int8_t drm_log_handle(uint8_t *index, rtc_driver_t *rtc_time)
{
    FIL fil;

    /* 判断非法文件,创建新文件 */
    char *ptr_file = file_get_log_name(*index);
    if ((ptr_file == NULL) || (strstr(ptr_file, ".log") == NULL))
    {
        file_create_log_name(ptr_file, rtc_time);
        int res = ff15_port_open_file(&fil, ptr_file, FA_CREATE_NEW);
        if (res != FR_OK)
        {
            DRM_LOGE("file create log = %d .", res);
            return -1;
        }
        ff15_port_close_file(&fil);
        DRM_LOGD("file illegal [%d];create file [%s] ;", *index, ptr_file);
    }

    /* (队列还差一帧就存满 || 队列中有数据,并且有60秒没有写过数据) */
    static uint32_t write_log_tick = 0;
    int16_t len = drm_log_get_queue_length();

#define LOG_WRITE_TICK_TIMES (60 * configTICK_RATE_HZ)
    if ((len > (DRM_LOG_BUFFER_LEN - (0xff + 64))) ||
        ((len > 0) && ((xTaskGetTickCount() - write_log_tick) >= LOG_WRITE_TICK_TIMES)))
    {
        write_log_tick = xTaskGetTickCount();
        int err = ff15_port_open_file(&fil, ptr_file, (FA_OPEN_APPEND | FA_WRITE)); /* 打开文件,并将光标移动到文件尾 */
        if (err != FR_OK)
        {
            if (err == FR_LOCKED) /* 1.加锁,则不操作文件 2.其他问题,直接删除文件 */
            {
                return 0;
            }
            else
            {
                int res = ff15_port_unlink_file(ptr_file);
                if (res != FR_OK)
                {
                    DRM_LOGE("delete current file [%s] res = %d err = %d.", ptr_file, err, res);
                    return -2;
                }
                else
                {
                    DRM_LOGE("delete current file [%s] err = %d.", ptr_file, err);
                    memset(ptr_file, '\0', strlen(ptr_file));
                    *index = (*index + 1) % FILE_DIR_LOG_NUMBER;
                    return -3;
                }
            }
        }

        /* 写缓存数据到文件中 */
        uint8_t *buff = pvPortMalloc(len);
        if (buff == NULL)
        {
            DRM_LOGE("write log file buff is null.");
            return -4;
        }
        uint16_t bw = 0;
        drm_log_queue_puts_data(buff, len);

        err = ff15_port_write_file(&fil, buff, len, &bw);
        if ((err != FR_OK) || (bw != len))
        {
            vPortFree(buff);
            DRM_LOGW("drm write [%s] failed; index = %d err = %d bw = %d;", ptr_file, *index, err, bw);
            return -5;
        }
        else
        {
            uint32_t size = ff15_port_size_file(&fil);
            // DRM_LOGI("index [%d] write [%s] size [%d];", *index, ptr_file, size);
        }
        vPortFree(buff);
        ff15_port_close_file(&fil);

        return 1;
    }
    return 0;
}

/**
 * @brief 串口输出
 * @param log等级
 * @param 消息
 * @return int32_t
 */
static void drm_console_logger(ulog_level_t severity, char *time, char *msg)
{
    printf("%s [%s]: %s\r\n",
           time,
           ulog_level_name(severity),
           msg);
}

/**
 * @brief 将log存入队列
 * @param log等级
 * @param 消息
 * @return void
 */
static void drm_cache_logger(ulog_level_t severity, char *time, char *msg)
{
    if (drm_log_buff == NULL)
    {
        DRM_LOGE("drm log cache is null.");
        return;
    }
    int len = snprintf(drm_log_buff, ULOG_MAX_MESSAGE_LENGTH,
                       "%s [%s]: %s\n",
                       time,
                       ulog_level_name(severity),
                       msg);
    drm_log_write_to_queue(drm_log_buff, len);
}

/**
 * @brief jlink rtt输出
 * @param log等级
 * @param 消息
 * @return int32_t
 */
static void drm_jlink_rtt_logger(ulog_level_t severity, char *time, char *msg)
{
    SEGGER_RTT_printf(0, "%s [%s]: %s\r\n",
                      time,
                      ulog_level_name(severity),
                      msg);
}